/**
 * Collaborative Story Development Web Application (CSDApp) 
 * Copyright Anton Strack 2014
 *
 * This file is part of Collaborative Story Development Application (CSDApp).
 *
 * CSDApp is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 *
 * CSDApp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * CSDApp. If not, see <http://www.gnu.org/licenses/>.
 *
 */
package csdwa;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This object will hold the acl list for a particular user role and group of
 * resource types that have the same parent resource type.
 *
 * @author Anton Strack
 */
public class AclGroupDao implements AclDaoPattern {

    private Map<String, Boolean> al;
    private int parentResourceTypeId = 0;
    private int userRoleId = 0;
    private Connection conn = null;
    private String getACLQuery = "SELECT * FROM ACL, ResourceType WHERE RoleID = ? AND ACL.ResourceID = ResourceType.ID AND ResourceType.ParentID = ?";

    /**
     * Construct the AclGroupDao with the minimum setup. The populateACL()
     * method will still need to be called for proper function.
     *
     * @param conn A pre-configured connection to the database.
     * @param resourceTypeId the primary resource type id.
     * @param userRoleId the user role id
     */
    public AclGroupDao(Connection conn, int resourceTypeId, int userRoleId) {
        this.conn = conn;
        this.al = new HashMap<String, Boolean>();
        this.setResourceTypeId(resourceTypeId);
        this.setUserRoleId(userRoleId);
    }

    /**
     * Construct the AclGroupDao with the option of populating the acl right
     * away.
     *
     * @param conn A pre-configured connection to the database.
     * @param resourceTypeId the primary resource type id.
     * @param userRoleId the user role id
     * @param populate true to populate the acl.
     */
    public AclGroupDao(Connection conn, int resourceTypeId, int userRoleId, boolean populate) {
        this(conn, resourceTypeId, userRoleId);
        if (populate == true) {
            this.populateACL();
        }
    }

    /**
     * Sets the primary resource type id for use in querying the acl table for
     * related rows.
     *
     * @param resourceTypeId the parent resource id that is shared by the
     * resources that will have the actions executed on by the role.
     */
    public final void setResourceTypeId(int parentResourceTypeId) {
        this.parentResourceTypeId = parentResourceTypeId;
    }

    /**
     * The user role the access control list is for. Each ACl implementing
     * object is specific to one user role.
     *
     * @param userRoleId the role id used in querying the acl table
     */
    public final void setUserRoleId(int userRoleId) {
        this.userRoleId = userRoleId;
    }

    /**
     * Get the primary resource type id used for querying the acl table.
     *
     * @return the primary resource type id
     */
    public int getResourceTypeId() {
        return this.parentResourceTypeId;
    }

    /**
     * Get the user role id used for querying the acl table and used by the
     * isAllowed() method
     *
     * @return the user role id
     */
    public int getUserRoleId() {
        return this.userRoleId;
    }

    /**
     * Populates the AclDaoPattern object with the access control list for the
     * user role * id all resource types that have the same parent resource type
     * and the parent resource type equals the resource type id set in this
     * object. This method must be called to populate access control list,
     * otherwise all calls to isAllowed() will return false.
     */
    @Override
    public void populateACL() {
        ResultSet rs = null;
        PreparedStatement ps = null;
        try {
            ps = conn.prepareStatement(this.getACLQuery);
            ps.setInt(1, this.userRoleId);
            ps.setInt(2, this.parentResourceTypeId);
            int rowCount = 0;
            rs = ps.executeQuery();
            StringBuilder sb;
            while (rs.next()) {
                // user.setName(user.getName());
                sb = new StringBuilder();
                sb.append(rs.getInt("ResourceID")).append(rs.getInt("ActionID")).append(rs.getInt("RoleID")).append(rs.getInt("IsOwned"));
                this.al.put(sb.toString(), rs.getBoolean("IsAllowed"));
                rowCount++;
            }
        } catch (SQLException ex) {
            Logger.getLogger(Authenticator.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                rs.close();
                ps.close();
            } catch (SQLException ex) {
                Logger.getLogger(Authenticator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * This method will not work properly for AclGroupDao objects and cannot be
     * used. Use the three argument version of isAllowed() instead.
     *
     * @param ActionId N/A
     * @param isOwned N/A
     * @throws UnsupportedOperationException
     */
    @Override
    public boolean isAllowed(int ActionId, int isOwned) throws UnsupportedOperationException {
        throw new UnsupportedOperationException("AclGroupDao.isAllowed() must use the three parameter isAllowed() method as the object can't determine the resourceId on it's own.");
    }

    /**
     * This method performs the actual acl authorization according to the
     * object's acl list and the values passed in to it.
     *
     * @param ActionId the action id representing the action to be preformed by
     * the role on the resource
     * @param isOwned a 1 or 0 integer indicating if the resource is owned by
     * the user with the current role. Ownership must be determined beforehand.
     * @param resourceId the direct resource type id that the action is to be
     * executed on. This is for instances where the primary resource id is set
     * as a parent resource type for a group of related resources.
     * @return
     */
    @Override
    public boolean isAllowed(int ActionId, int isOwned, int resourceId) {
        boolean allowed = false;
        StringBuilder sb = new StringBuilder();
        sb.append(resourceId).append(ActionId).append(this.userRoleId).append(isOwned);
        String key = sb.toString();
        if (this.al.containsKey(key) && this.al.get(key) != null) {
            allowed = this.al.get(key);
        }
        return allowed;
    }
}
